package com.gitee.apanlh.util.net.http;

import com.gitee.apanlh.exp.HttpResponseException;
import com.gitee.apanlh.exp.ParseException;
import com.gitee.apanlh.util.base.Empty;
import com.gitee.apanlh.util.base.Eq;
import com.gitee.apanlh.util.base.StringUtils;
import com.gitee.apanlh.util.dataformat.JsonUtils;
import com.gitee.apanlh.util.encode.StrEncodeUtils;
import com.gitee.apanlh.util.net.http.io.HttpResponseInputStream;
import com.gitee.apanlh.util.net.http.log.HttpLog;
import com.gitee.apanlh.util.valid.ValidParam;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.List;

/**	
 * 	HTTP请求响应体
 * 	<br>自动关闭客户端及相关流
 * 	
 * 	@author Pan
 */
public class HttpResponse implements BaseHttpStatus {
	
	/** HTTP请求对象 */
	private HttpRequest httpRequest;
	/** HTTP客户端 */
	private HttpClient httpClient;
	/** HTTP请求状态*/
	private HttpStatus httpStatus;
	/** HTTP响应头*/
	private HttpHeader<List<String>> httpResponseHeader;
	/** 响应输入流 */
	private HttpResponseInputStream receiveInputSteam;
	/** HTTP日志 */
	private HttpLog httpLog;
	/** 响应内容 */
	private byte[] content;
	
	/**
	 * 	默认-构造函数
	 * 
	 * 	@author Pan
	 */
	HttpResponse() {
		super();
	}
	
	/**	
	 * 	构造函数
	 * 	<br>读取服务响应消息
	 * 	<br>创建响应状态
	 * 	<br>自动关闭客户端
	 * 	
	 * 	@author Pan
	 * 	@param 	httpRequest			HTTP请求对象
	 * 	@param 	httpStatus			HTTP响应状态对象
	 * 	@param 	httpClient			HTTP客户端
	 * 	@param 	receiveInputSteam	服务响应消息流
	 * 	@param 	httpLog				HTTP日志
	 */
	public HttpResponse(HttpRequest httpRequest, HttpStatus httpStatus, HttpClient httpClient, HttpResponseInputStream receiveInputSteam, HttpLog httpLog) {
		this.httpRequest = httpRequest;
		this.httpStatus = httpStatus;
		this.httpClient = httpClient;
		this.receiveInputSteam = receiveInputSteam;
		this.httpLog = httpLog;
		//	读取响应
		read();
		//	检测是否错误响应
		checkErrorResponse();
	}
	
	/**	
	 * 	读取响应返回信息
	 * 	<br>自动关闭客户端及流
	 * 	
	 * 	@author Pan
	 */
	protected void read() {
		try {
			this.content = receiveInputSteam.readResponse();
		} finally {
			httpClient.close();
		}
	}
	
	/**	
	 * 	自定类型响应体转换数据
	 * 	<br>仅支持String，byte两种数据
	 * 	
	 * 	@author Pan
	 * 	@param  <T>     数据类型
	 * 	@param 	charset	编码
	 * 	@param 	clazz	类
	 * 	@return	T
	 */
	@SuppressWarnings("unchecked")
	protected <T> T getResponseBody(String charset, Class<T> clazz) {
		if (ValidParam.isNotNull(getErrorMsg())) {
			checkErrorResponse();
			throw new HttpResponseException(StringUtils.format("get response body error"));
		}
		
		T result = null;
		
		//	获取String
		if (Eq.classes(clazz, String.class)) {
			//	如果先获取byte在获取str则转换String
			if (ValidParam.isEmpty(this.content)) {
				result = (T) Empty.str();
			} else {
				result = (T) StrEncodeUtils.utf8EncodeToStr(this.content, charset);
			}
		}
		
		//	获取byte[]
		if (Eq.classes(clazz, byte[].class)) {
			result = (T) this.content;
		}

		httpLog.writeResponse(result);
		
		return result;
	}
	
	/**	
     * 	将响应体转换成byte[]格式
     * 
     * 	@author Pan
     * 	@return	byte[]
     */
    public byte[] getByte() {
    	return getResponseBody(null, byte[].class);
    }
	
	/**	
     * 	将响应体转换成Bean
     * 	<br>仅限解析JSON
     * 	
     * 	@author Pan
	 * 	@param 	<T>		返回类型
	 * 	@param 	clazz	类
     * 	@return	T
     */
	public <T> T getBean(Class<T> clazz) {
		String str = getStr();
		boolean hasJson = JsonUtils.hasJson(str);
		if (!hasJson) {
			throw new ParseException(StringUtils.format("parse json error please check param:{}", str));
		}
		return JsonUtils.toBean(str, clazz);
	}

	/**	
     * 	将响应体转换成ListBean
     * 	<br>仅限解析JSON
     * 
     * 	@author Pan
	 * 	@param 	<T>		返回类型
	 * 	@param 	clazz	类
     * 	@return	T
     */
	public <T> List<T> getBeanList(Class<T> clazz) {
		String str = getStr();
		boolean hasJson = JsonUtils.hasJson(str);
		if (!hasJson) {
			throw new ParseException(StringUtils.format("parse json error please check param:{}", str));
		}
		return JsonUtils.toList(str, clazz);
	}
	
	/**	
	 * 	将响应体转换成String格式
	 * 	<br>默认UTF-8
	 * 	<br>将自动关闭HTTP客户端
	 * 
	 * 	@author Pan
	 * 	@return	String
	 */
	public String getStr() {
		return getStr(httpRequest.getConfig().getCharset());
	}
	
	/**	
	 * 	将服务响应消息转换成String格式
	 * 
	 * 	@author Pan
	 * 	@param	charset	自定义字符编码集
	 * 	@return	String
	 */
	public String getStr(String charset) {
		return getResponseBody(charset, String.class);
	}
	   
    /**	
     * 	获取响应状态对象
     * 	
     * 	@author Pan
     * 	@return	HttpStatus
     */
    public HttpStatus getStatus() {
    	return httpStatus;
    }
    
	/**	
     * 	将服务响应流复制返回
     * 	<br>需要自行关闭流
     * 	
     * 	@author Pan
     * 	@return	InputStream
     */
    public InputStream getInputStream() {
    	return new ByteArrayInputStream(content);
    }
 
	/**
	 * 	获取HTTP请求对象
	 * 	
	 * 	@author Pan
	 * 	@return	HttpRequest
	 */
	public HttpRequest getHttpRequest() {
		return httpRequest;
	}
	
	/**
	 * 	获取HTTP响应头
	 * 	
	 * 	@author Pan
	 * 	@return	httpResponseHeader
	 */
	public HttpResponseHeader getHttpResponseHeader() {
		if (httpResponseHeader == null) {
			this.httpResponseHeader = HttpResponseHeader.create(httpClient.getClient());
		}
		return (HttpResponseHeader) httpResponseHeader;
	}
	
	/**
	 * 	检测是否错误响应
	 * 	<br>4xx及5xx响应
	 * 	<br>将记录ERROR级别的错误日志
	 * 	
	 * 	@author Pan
	 */
	private void checkErrorResponse() {
		if (receiveInputSteam.hasErrorInputStream()) {
			httpStatus.setErrorMsg(this.content, httpRequest.getConfig().getCharset());
			httpLog.writeError(httpStatus.getErrorMsg());
		}
	}

	/**
	 * 	获取HTTPLog对象
	 *
	 * 	@author Pan
	 * 	@return	HttpLog
	 */
	public HttpLog getHttpLog() {
		return this.httpLog;
	}

	@Override
	public String getMessage() {
		return httpStatus.getMessage();
	}

	@Override
	public String getErrorMsg() {
		return httpStatus.getErrorMsg();
	}

	@Override
	public Integer getCode() {
		return httpStatus.getCode();
	}

	@Override
	public boolean isSuc() {
		return httpStatus.isSuc();
	}

	@Override
	public boolean isError() {
		return httpStatus.isError();
	}
}
